//REQUIRED LIBRARIES
#include <LedControl.h>                     //Include LedControl.h for controlling the LED displays
#include <Button.h>                         //Include Button.h for listening to Buttons
#include <TimerOne.h>						//Include TimerOne.h for tempo timing using internal interrupt
#include <Step.h>


//DECLARE CONSTANTS
const byte totalSteps = 8;					//The total number of steps in the sequence
const byte totalOutputs = 8;				//The total number of outputs


//DECLARE PINS
//Digital
const byte tempoLED = 13;					//Pin for visually showing the tempo
const byte triggerVCLatch = 12;				//Pin for outputing VC/Trigger levels
const byte buttonLatchPin = 11;				//Pin for switching button shift registers on
const byte displayLatchPin = 10;			//Pin for outputting display data
const byte dataOutPin = 4;                  //Pin for ALL data shifting out (YELLOW)
const byte dataInPin = 5;                   //Pin for ALL data shifting in (YELLOW)
const byte clockPin = 3;                    //Pin for clocking ALL data shifting (ORANGE)
//Analogue
const byte tempoPin = 0;					//Tempo pot wiper pin (Analogue)
const byte outputPosPin = 1;				//Reads which row/output the buttons are controlling


//DECLARE VARIABLES
volatile byte stepPosition = 0;             //Stores the stepPosition 0-totalSteps
int tempoVal = 10;							//Variable to store tempo reading from tempo pot
long setPeriod = 10;						//Stores the timeperiod of each step (claculated using tempoVal, tempoMax & tempoScale)
int tempoMax = 30000;						//Sets the highest tempo value (the smaller this number the higher the max tempo)
int tempoScale = 200;						//How much to stretch the tempo scale (the bigger this number the more range the tempo has) 
int seqOutputs[totalSteps][totalOutputs];   //Array to store voltage control/trigger values
int outputPos = 0;							//The output that is being editted

//Define variables to hold the data 
//for each shift register.
//starting with non-zero numbers can help
//troubleshoot
byte switchVar1 = 72;						//01001000
Button buttons[totalSteps];					//An array of Button objects, one for each step
byte readRegisterValue1;					//Variable for holding the first button register values
byte readRegisterValue2;					//Variable for holding the second button register values

//Create LedControl object
LedControl lc=LedControl(dataOutPin,clockPin,displayLatchPin,1); // lc is our object
// dataPin is connected to the MAX7219 pin 1
// clockPin is connected to the CLK pin 13
// displayLatchPin is connected to LOAD pin 12
// 1 as we are only using 1 MAX7219



// ################################ SETUP #############################################
void setup() {
	//start serial
	Serial.begin(9600);

	//Setup the pin modes
	pinMode(tempoLED, OUTPUT);
	pinMode(triggerVCLatch, OUTPUT);
	pinMode(buttonLatchPin, OUTPUT);
	pinMode(dataOutPin, OUTPUT);
	pinMode(dataInPin, INPUT);
	pinMode(clockPin, OUTPUT);

	//Setup interrupt using Timer1
	Timer1.initialize(setPeriod);				// set a timer of length 100000 microseconds (or 0.1 sec - or 10Hz => the led will blink 5 times, 5 cycles of on-and-off, per second)
	Timer1.attachInterrupt(incereaseStep);		// attach the service routine here

	// the zero refers to the MAX7219 number, it is zero for 1 chip
	lc.shutdown(0,false);// turn off power saving, enables display
	lc.setIntensity(0,8);// sets brightness (0~15 possible values)
	lc.clearDisplay(0);// clear screen


	//Zero all trigger/vc values in seqOutputs array
	for(int i = 0; i < totalSteps; i++){
		for(int j = 0; j < totalOutputs; j++){
			seqOutputs[i][j] = 0;
		}
	}

	//Setup the step buttons
	for(int i =0; i < totalSteps; i++){
		buttons[i].setupButton(i,HIGH,&switchVar1);	//switchVar1 stores the first byte shifted in for the step buttons (first eight buttons)
	}
}



// ################################ LOOP ##############################################
void loop() {
  //Set all voice trigger pins LOW
  setTriggersLow();

  //Test which output row is being edited
  outputPos = getOutputPosition(analogRead(outputPosPin));

  //Shift in the button data
  shiftInButtonData();

  // #Deal with button presses

  // #Output voltage levels on VC pins
	lc.clearDisplay(0);
	lc.setColumn(0, outputPos, B11111111);
  
  // #Light LED's
	

  //Set the tempo time period
  tempoVal = analogRead(tempoPin);
  setPeriod = long(tempoVal) * long(tempoScale) + long(tempoMax);
  Timer1.setPeriod(setPeriod);

}


// ############################### METHODS ############################################

/* setTriggersLow():
 set all trigger pins low */
void setTriggersLow(){
  /*digitalWrite(trigger1Pin, LOW);  
  digitalWrite(trigger2Pin, LOW);                  
  digitalWrite(trigger3Pin, LOW);                  
  digitalWrite(trigger4Pin, LOW);                  
  digitalWrite(trigger5Pin, LOW);*/                  
}


/* getOutputPosition: 
 this method calculates which output channel is being edited.  interval
is the the difference in analoue reading between each output channel.
HalfInt is added to the value before deviding makesure the returned
value is not to near the margin between each different value*/
int getOutputPosition(int value) {
	int interval = 1023 / totalOutputs;
	int halfInt = interval / 2;

	return (value + halfInt)/interval;
}


/* shiftInButtonData: 
 this method calls shiftIn to collect button data and then updates 
 the button objects and does some processing of the data */
void shiftInButtonData(){
	//Pulse the latch pin:
	//set HIGH to transmit data serially  
	digitalWrite(buttonLatchPin,HIGH);

	//while the shift register is in serial mode
	//collect each shift register into a byte
	//the register attached to the chip comes in first 
	switchVar1 = shiftIn(dataInPin, clockPin);

	//set LOW to collect parallel data
	digitalWrite(buttonLatchPin,LOW);

	//Cycle through each button and listen to it and output some debugging info
	for(int i = 0; i < totalSteps; i++){
		buttons[i].listen();

		if(buttons[i].isPressed()){
			Serial.print("B");
			Serial.print(i);
			Serial.println(": Pressed");
		}
	
		if(buttons[i].isHold()){
			Serial.print("B");
			Serial.print(i);
			Serial.println(": Held");
		}
	
		if(buttons[i].onDoubleClick()){
			Serial.print("B");
			Serial.print(i);
			Serial.println(": Double Click");
		}
	}
}


/* shiftIn: 
 this method shifts in and returns a byte of data from the button shift register/s */
byte shiftIn(int myDataPin, int myClockPin) { 
  int i;
  int temp = 0;
  int pinState;
  byte myDataIn = 0;

  pinMode(myClockPin, OUTPUT);
  pinMode(myDataPin, INPUT);

  //we will be holding the clock pin high 8 times (0,..,7) at the
  //end of each time through the for loop

  //at the begining of each loop when we set the clock low, it will
  //be doing the necessary low to high drop to cause the shift
  //register's DataPin to change state based on the value
  //of the next bit in its serial information flow.
  //The register transmits the information about the pins from pin 7 to pin 0
  //so that is why our function counts down
  for (i=7; i>=0; i--)
  {
	digitalWrite(myClockPin, 0);
	delayMicroseconds(2);
	temp = digitalRead(myDataPin);
	if (temp) {
	  pinState = 1;
	  //set the bit to 0 no matter what
	  myDataIn = myDataIn | (1 << i);
	}
	else {
	  //turn it off -- only necessary for debuging
	  //print statement since myDataIn starts as 0
	  pinState = 0;
	}

	//Debuging print statements
	//Serial.print(pinState);
	//Serial.print("     ");
	//Serial.println (dataIn, BIN);

	digitalWrite(myClockPin, 1);

  }
  //debuging print statements whitespace
  //Serial.println();
  //Serial.println(myDataIn, BIN);
  return myDataIn;
}


// ############################## INTERRUPT ###########################################


/* incereaseStep: 
 this is an interrupt method called on each interal interrupt */
void incereaseStep(){

  //increase the stepPosition by one place
  stepPosition = stepPosition + 1;

  //check if the stepPosition is greater than totalSteps
  if(stepPosition >= totalSteps){
	// and go back to the start if it is
	stepPosition = 0;
  }

  //#Set voice trigger pins HIGH  
}

